Coverage Report

Created: 2024-12-19 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\compiler\union.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::compiler::message::{Referenced, SizeInfo};
30
use crate::compiler::structure::{Field, FieldType, FieldView, FixedField, Structure};
31
use crate::compiler::util::store::name_index;
32
use crate::compiler::{Error, Protocol};
33
use crate::model::protocol::Description;
34
use std::rc::Rc;
35
36
#[derive(Clone, Debug)]
37
pub struct UnionField {
38
    pub name: String,
39
    pub description: Option<Description>,
40
    pub case: usize,
41
    pub item_type: Option<Referenced>,
42
}
43
44
impl UnionField {
45
36
    pub fn from_model(
46
36
        proto: &Protocol,
47
36
        discriminant: &FixedField,
48
36
        value: crate::model::union::UnionField,
49
36
    ) -> Result<Self, Error> {
50
36
        let case: usize = match &discriminant.view {
51
0
            FieldView::Float { .. } => return Err(Error::FloatInUnionDiscriminant),
52
28
            FieldView::Enum(r) => {
53
28
                r.variants_map.get(&value.case).copied().ok_or(Error::InvalidUnionCase(value.case))
?0
as usize
54
            }
55
            //TODO: Check how unions work with the new bits-type/raw-type/view-type separation
56
8
            FieldView::None => value.case.parse().map_err(|_| 
Error::InvalidUnionCase(value.case)0
)
?0
,
57
        };
58
36
        let item_type = value
59
36
            .item_type
60
36
            .map(|v| 
Referenced::lookup(proto, &v).ok_or(Error::UndefinedReference(v))26
)
61
36
            .transpose()
?0
;
62
36
        Ok(UnionField {
63
36
            name: value.name,
64
36
            description: value.description,
65
36
            case,
66
36
            item_type,
67
36
        })
68
36
    }
69
}
70
71
#[derive(Clone, Debug)]
72
pub struct DiscriminantField {
73
    pub root: Rc<Structure>,
74
    pub leaf: Rc<Structure>,
75
    pub leaf_index: usize,
76
    pub index_list: Vec<usize>,
77
}
78
79
struct DiscriminantFieldIterator<'a> {
80
    cur: &'a Structure,
81
    index: std::slice::Iter<'a, usize>,
82
}
83
84
impl<'a> Iterator for DiscriminantFieldIterator<'a> {
85
    type Item = (&'a Field, bool);
86
87
28
    fn next(&mut self) -> Option<Self::Item> {
88
28
        let 
index16
= self.index.next()
?12
;
89
16
        let field = &self.cur.fields[*index];
90
16
        let is_leaf = match &field.ty {
91
12
            FieldType::Fixed(_) => true,
92
0
            FieldType::Array(_) => std::unreachable!(),
93
4
            FieldType::Struct(v) => {
94
4
                self.cur = v;
95
4
                false
96
            }
97
        };
98
16
        Some((field, is_leaf))
99
28
    }
100
}
101
102
impl DiscriminantField {
103
12
    pub fn iter(&self) -> impl Iterator<Item = (&Field, bool)> {
104
12
        DiscriminantFieldIterator {
105
12
            cur: &self.root,
106
12
            index: self.index_list.iter(),
107
12
        }
108
12
    }
109
110
0
    pub fn get_leaf(&self) -> &Field {
111
0
        &self.leaf.fields[self.leaf_index]
112
0
    }
113
114
42
    pub fn get_leaf_fixed(&self) -> &FixedField {
115
42
        self.leaf.fields[self.leaf_index].ty.as_fixed().unwrap()
116
42
    }
117
118
10
    pub fn from_model(proto: &Protocol, discriminant: String) -> Result<Self, Error> {
119
10
        let mut parts = discriminant.split(".");
120
10
        let name = parts.next().ok_or(Error::InvalidUnionDiscriminant)
?0
;
121
10
        let mut leaf = proto.structs.get(name).ok_or_else(|| 
Error::UndefinedReference(name.into())0
)
?0
;
122
10
        let root = leaf;
123
10
        let mut index_list = Vec::new();
124
12
        for sub in parts {
125
12
            let (index, field) = leaf
126
12
                .fields
127
12
                .iter()
128
12
                .enumerate()
129
12
                .find(|(_, v)| v.name == sub)
130
12
                .ok_or_else(|| 
Error::UndefinedReference(format!("{}.{}", name, sub))0
)
?0
;
131
12
            index_list.push(index);
132
12
            match &field.ty {
133
10
                FieldType::Fixed(_) => break,
134
2
                FieldType::Struct(v) => leaf = v,
135
0
                FieldType::Array(_) => return Err(Error::InvalidUnionDiscriminant),
136
            }
137
        }
138
10
        Ok(DiscriminantField {
139
10
            root: root.clone(),
140
10
            leaf: leaf.clone(),
141
10
            leaf_index: index_list.last().copied().unwrap(),
142
10
            index_list,
143
10
        })
144
10
    }
145
}
146
147
#[derive(Clone, Debug)]
148
pub struct Union {
149
    pub name: String,
150
    pub discriminant: DiscriminantField,
151
    pub description: Option<Description>,
152
    pub cases: Vec<UnionField>,
153
    pub size: SizeInfo,
154
}
155
156
impl Union {
157
13
    pub fn has_content(&self) -> bool {
158
22
        self.cases.iter().any(|v| v.item_type.is_some())
159
13
    }
160
161
10
    pub fn from_model(proto: &Protocol, value: crate::model::union::Union) -> Result<Self, Error> {
162
10
        let discriminant = DiscriminantField::from_model(proto, value.discriminant)
?0
;
163
10
        let cases = value
164
10
            .cases
165
10
            .into_iter()
166
36
            .map(|v| UnionField::from_model(proto, discriminant.get_leaf_fixed(), v))
167
10
            .collect::<Result<Vec<UnionField>, Error>>()
?0
;
168
36
        let 
is_element_dyn_sized = cases.iter().any(10
|v| {
169
36
            v.item_type
170
36
                .as_ref()
171
36
                .map(|v| 
match v26
{
172
22
                    Referenced::Struct(_) => false,
173
4
                    Referenced::Message(v) => v.size.is_element_dyn_sized,
174
36
                
}26
)
175
36
                .unwrap_or_default()
176
36
        });
177
16
        let is_dyn_sized = cases.iter().any(|v| {
178
16
            v.item_type
179
16
                .as_ref()
180
16
                .map(|v| 
match v6
{
181
2
                    Referenced::Struct(_) => false,
182
4
                    Referenced::Message(v) => v.size.is_dyn_sized,
183
16
                
}6
)
184
16
                .unwrap_or_default()
185
16
        });
186
10
        Ok(Union {
187
10
            name: value.name,
188
10
            discriminant,
189
10
            description: value.description,
190
10
            cases,
191
10
            size: SizeInfo {
192
10
                is_element_dyn_sized,
193
10
                is_dyn_sized,
194
10
            },
195
10
        })
196
10
    }
197
}
198
199
name_index!(Union => name);